package mdc

import (
	"errors"
	"go.uber.org/zap"
	"sync"
	"wzgames/internal/gloader"
	"wzgames/internal/model/gamep"
	"wzgames/internal/services/games"
	"wzgames/pkg/logger"
	"wzgames/pkg/remote/etcd"
	"wzgames/pkg/utils/stringutil"
)

var (
	instMgr  *InstManager
	onceInst sync.Once
)

type InstManager struct {
	mu sync.RWMutex

	instants map[int64]games.IGameService
}

func InitInstManager() *InstManager {
	if instMgr == nil {
		onceInst.Do(func() {
			instMgr = &InstManager{
				instants: make(map[int64]games.IGameService),
			}
		})
	}
	return instMgr
}

// OnInstChange 游戏实例 动态更新
func (st *InstManager) OnInstChange(instId int64, evt etcd.KvEvtType) {
	switch evt {
	case etcd.KvEventNew:
		gmInst := gloader.GameMgr().GetGameInst(instId)
		if gmInst == nil {
			logger.Error("[WSC] init new game instance fail on GetGameInst", zap.Int64("InstID", instId))
		} else {
			err := st.InitInst(gmInst)
			if err != nil {
				logger.Error("[WSC] init new game instance fail on InitInst", zap.Int64("InstID", instId))
			}
		}
	case etcd.KvEventUpdate:
		st.mu.RLock()
		inst, ok := st.instants[instId]
		st.mu.RUnlock()
		if ok {
			gmInst := gloader.GameMgr().GetGameInst(instId)
			if gmInst != nil {
				inst.SetGameInst(gmInst)
				inst.Update()
			}
		}
	case etcd.KvEventDelete:
		st.mu.RLock()
		inst, ok := st.instants[instId]
		st.mu.RUnlock()
		if ok {
			inst.Destroy()
			st.mu.Lock()
			delete(st.instants, instId)
			st.mu.Unlock()
		}
	}
}

func (st *InstManager) InitInst(inst *gamep.GameInst) error {
	st.mu.Lock()
	defer st.mu.Unlock()
	if _, ok := st.instants[inst.InstID]; ok {
		return errors.New("game instant already init")
	}
	if len(inst.NodeTags) > 0 && !stringutil.StringArrContains(inst.NodeTags, gloader.BootConfig().NodeKey()) {
		return errors.New("game instant not allow on this node")
	}
	a, err := games.NewGGame(inst)
	if err != nil {
		logger.Error("err == ", zap.String("error", err.Error()))
		return err
	}

	st.instants[inst.InstID] = a

	return nil
}

func (st *InstManager) GetInst(instId int64) games.IGameService {
	st.mu.RLock()
	v, ok := st.instants[instId]
	st.mu.RUnlock()
	if ok {
		return v
	}

	return nil
}

func (st *InstManager) Destroy() {
	st.mu.Lock()
	defer st.mu.Unlock()
	for _, v := range st.instants {
		v.Destroy()
	}
}
